package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SearchService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.lang.annotation.Native;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService{
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public Map search(Map<String, String> searchMap) {
        //封装结果
        HashMap<Object, Object> resultMap = new HashMap<>();
        //构建查询条件的封装对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQuery= QueryBuilders.boolQuery();
        //名称的分词查询
        if(!StringUtils.isEmpty(searchMap.get("keywords"))){
            boolQuery.must(QueryBuilders.matchQuery("name",searchMap.get("keywords")).operator(Operator.AND));
        }
        //品牌的精确查询
        if(!StringUtils.isEmpty(searchMap.get("brand"))){
            boolQuery.filter(QueryBuilders.termQuery("brandName",searchMap.get("brand")));
        }

        //查询规格参数
        Set<String> keySet = searchMap.keySet();
        for (String key : keySet) {
            if(key.startsWith("spec_")){
                //重新拼接查询字段
                String specMap_String_keyword="specMap."+key.substring(5)+".keyword";
                //查询的值
                String value = searchMap.get(key.replace("%2B","+"));
                boolQuery.filter(QueryBuilders.termQuery(specMap_String_keyword,value));
            }
        }

        //价格区间查询
        if(!StringUtils.isEmpty(searchMap.get("price"))){
            String[] prices = searchMap.get("price").split("-");
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(prices[0]));
            if (prices.length>1){
                boolQuery.filter(QueryBuilders.rangeQuery("price").lte(prices[1]));
            }
        }

        //封装查询条件
        //基本查询
        nativeSearchQueryBuilder.withQuery(boolQuery);

        //品牌的聚合查询,(聚合查询和基本查询是平级的关系)
        String brands="brands";
        TermsAggregationBuilder brandAggregationBuilder = AggregationBuilders.terms(brands).field("brandName").size(100);
        nativeSearchQueryBuilder.addAggregation(brandAggregationBuilder);

        //规格的聚合查询
        String specs="specs";
        TermsAggregationBuilder specsAggregationBuilder = AggregationBuilders.terms(specs).field("spec.keyword").size(100);
        nativeSearchQueryBuilder.addAggregation(specsAggregationBuilder);

        //分页查询
        String pageNum=searchMap.get("pageNum");
        String pageSize=searchMap.get("pageSize");
        if (StringUtils.isEmpty(searchMap.get("pageNum"))){
             pageNum="1";
        }
        if (StringUtils.isEmpty(searchMap.get("pageSize"))){
             pageSize="30";
        }
        nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.parseInt(pageNum)-1,Integer.parseInt(pageSize)));

        //排序查询
        if(StringUtils.isNotEmpty(searchMap.get("sortField"))&&StringUtils.isNotEmpty(searchMap.get("sortRule"))) {
            if ("ASC".equals(searchMap.get("sortRule"))){
                nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.ASC));
            }else {
                nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.DESC));
            }
        }

        //高亮查询
        HighlightBuilder.Field highlightBuilder = new HighlightBuilder.Field("name").preTags("<span style='color:red'>").postTags("</span>");
        nativeSearchQueryBuilder.withHighlightFields(highlightBuilder);

        //开启查询
        if(searchMap!=null){
            /**
             * 第一个参数,条件构建对象
             * 第二个参数,查询操作实体类
             * 第三个参数,查询结果封装对象
             */
            AggregatedPage<SkuInfo> resultInfo = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                    ArrayList<T> list = new ArrayList<>();
                    //获取查询命中结果数据
                    SearchHits hits = searchResponse.getHits();
                    if(hits!=null){
                        for (SearchHit hit : hits) {
                            SkuInfo skuInfo=JSON.parseObject(hit.getSourceAsString(),SkuInfo.class);
                            Map<String, HighlightField> highlightMap = hit.getHighlightFields();
                            if(highlightMap!=null&&highlightMap.size()>0) {
                                String highlightfragment = highlightMap.get("name").fragments()[0].toString();
                                skuInfo.setName(highlightfragment);
                            }
                            list.add((T)skuInfo);
                        }
                    }
                    return new AggregatedPageImpl<T>(list,pageable,hits.getTotalHits(),searchResponse.getAggregations());
                }
            });
            //聚合查询结果的两种处理方式
            //第一种原生api的处理方式(品牌桶聚合查询)
            Terms brandTerms = (Terms) resultInfo.getAggregations().asMap().get(brands);
            List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
            ArrayList<String> brandList = new ArrayList<>();
            for (Terms.Bucket bucket : buckets) {
                brandList.add(bucket.getKeyAsString());
            }
            //第二种函数式编程方式(流运算,规格桶聚合查询)
            StringTerms  specTerms= (StringTerms) resultInfo.getAggregation(specs);
            List<String> specList = specTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
            //封装查询结果
            resultMap.put("total",resultInfo.getTotalElements());
            resultMap.put("totalPages",resultInfo.getTotalPages());
            resultMap.put("rows",resultInfo.getContent());
            resultMap.put("brands",brandList);
            resultMap.put("specs",specList);
            resultMap.put("pageNum",pageNum);
        }
        return resultMap;
    }
}
